export type PermissionOptions = {
  writable?: boolean;
  mode?: 'read' | 'readwrite';
};

type PermissionHandle = {
  queryPermission: (options: PermissionOptions) => Promise<PermissionState>;
  requestPermission: (options: PermissionOptions) => Promise<PermissionState>;
};

export type InternalFileSystemHandle = FileSystemHandle & PermissionHandle;
export type InternalFileSystemFileHandle = FileSystemFileHandle & PermissionHandle;
export type InternalFileSystemDirectoryHandle = FileSystemDirectoryHandle & PermissionHandle;

/**
 * Open a handle to an existing file on the local file system.
 * Handle to the existing file.
 */
export async function getFileHandle(): Promise<InternalFileSystemFileHandle> {
  // For Chrome 86 and later...
  if ('showOpenFilePicker' in window) {
    const [handle] = await (window as any).showOpenFilePicker();
    return handle;
  }
  // For Chrome 85 and earlier...
  return (window as any).chooseFileSystemEntries();
}

export function getDirectoryHandle(): Promise<InternalFileSystemDirectoryHandle> {
  // For Chrome 86 and later...
  if ('showDirectoryPicker' in window) {
    return (window as any).showDirectoryPicker();
  }
  // For Chrome 85 and earlier...
  return (window as any).chooseFileSystemEntries();
}

/**
 * Create a handle to a new (text) file on the local file system.
 * Handle to the new file.
 */
export function getNewFileHandle(): Promise<InternalFileSystemFileHandle> {
  // For Chrome 86 and later...
  if ('showSaveFilePicker' in window) {
    const opts = {
      types: [
        {
          description: 'Text file',
          accept: { 'text/plain': ['.txt'] },
        },
      ],
    };
    return (window as any).showSaveFilePicker(opts);
  }
  // For Chrome 85 and earlier...
  const opts = {
    type: 'save-file',
    accepts: [
      {
        description: 'Text file',
        extensions: ['txt'],
        mimeTypes: ['text/plain'],
      },
    ],
  };
  return (window as any).chooseFileSystemEntries(opts);
}

/**
 * Reads the raw text from a file.
 */
export async function readFile(file: File): Promise<string> {
  // If the new .text() reader is available, use it.
  if (file.text) {
    return file.text();
  }
  // Otherwise use the traditional file reading technique.
  return readFileLegacy(file);
}

/**
 * Reads the raw text from a file.
 * @private
 */
export function readFileLegacy(file: File): Promise<string> {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.addEventListener('loadend', (e) => {
      const text = e.target?.result;
      resolve(text as string);
    });
    reader.readAsText(file);
  });
}

/**
 * Writes the contents to disk.
 */
export async function writeFile(fileHandle: FileSystemFileHandle, contents: FileSystemWriteChunkType) {
  // Support for Chrome 82 and earlier.
  if ((fileHandle as any).createWriter) {
    // Create a writer (request permission if necessary).
    const writer = await (fileHandle as any).createWriter();
    // Write the full length of the contents
    await writer.write(0, contents);
    // Close the file and write the contents to disk
    await writer.close();
    return;
  }
  // For Chrome 83 and later.
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

/**
 * Verify the user has granted permission to read or write to the file, if
 * permission hasn't been granted, request permission.
 */
export async function verifyPermission(fileHandle?: InternalFileSystemHandle, withWrite?: boolean) {
  if (!fileHandle) return false;
  const opts: PermissionOptions = {};
  if (withWrite) {
    opts.writable = true;
    // For Chrome 86 and later...
    opts.mode = 'readwrite';
  }
  // Check if we already have permission, if so, return true.
  if ((await fileHandle.queryPermission(opts)) === 'granted') {
    return true;
  }
  // Request permission to the file, if the user grants permission, return true.
  if ((await fileHandle.requestPermission(opts)) === 'granted') {
    return true;
  }
  // The user did nt grant permission, return false.
  return false;
}
